package edu.thu.thss.yxy.sip;

import java.util.*;

import javax.sip.Dialog;
import javax.sip.message.Request;

import edu.thu.thss.yxy.sip.event.CallStateEvent;
import edu.thu.thss.yxy.sip.event.CallStateListener;
import edu.thu.thss.yxy.util.Logger;

/**
 * Keeps a list of all calls currently active and maintained by this protocol
 * povider. Offers methods for finding a call by its ID, peer dialog
 * and others.
 * 
 * @author Andy Gao
 *
 */
public class ActiveCallsRepository implements CallStateListener {
	/**
     * Our class logger.
     */
    private static final Logger logger
        = Logger.getLogger(ActiveCallsRepository.class);
    
    /**
     * A table mapping call ids against call instances.
     */
    private Hashtable<String, Call> activeCalls
                                    = new Hashtable<String, Call>();
    
    /**
     * If <tt>evt</tt> indicates that the call has been ended we remove it from
     * the repository.
     * @param evt the <tt>CallStateEvent</tt> instance containing the source
     * calls and its old and new state.
     */
    public void callStateChanged(CallStateEvent evt)
    {
        if(evt.getNewState().equals(CallState.DISCONNECTED)
        		||evt.getNewState().equals(CallState.FAILED))
        {
            Call sourceCall = activeCalls.remove(evt.getSourceCall().getCallID());

            logger.trace("Removing call " + sourceCall + " from the list of "
                        + "active calls because it entered an ENDED state");
        }
    }

    /**
     * Returns an iterator over all currently active (non-ended) calls.
     *
     * @return an iterator over all currently active (non-ended) calls.
     */
    public Iterator<Call> getActiveCalls()
    {
        return new LinkedList<Call>(activeCalls.values()).iterator();
    }

    /**
     * Adds the specified call to the list of calls tracked by this repository.
     * @param call CallSipImpl
     */
    public void addCall(Call call)
    {
        activeCalls.put(call.getCallID(), call);
        call.addCallStateListener(this);
    }
    
	public Call createCall(Dialog dialog, Request initialRequest) {

		Call call = null;
		if (dialog.getDialogId() != null) {
			call = findCall(dialog);
		}
		if (call == null) {
			call = new Call();
		}
		call.setDialog(dialog);
		call.setInitialRequest(initialRequest);
		call.setCallState(CallState.DIALING);
		addCall(call);

		call.addCallStateListener(this);
		return call;

	}
    
    /**
     * Returns the call that contains the specified dialog (i.e. it is
     * established  between us and one of the other call peers).
     * <p>
     * @param dialog the jain sip <tt>Dialog</tt> whose containing call we're
     * looking for.
     * @return the <tt>CallSipImpl</tt> containing <tt>dialog</tt> or null
     * if no call contains the specified dialog.
     */
    public Call findCall(Dialog dialog)
    {
        Iterator<Call> activeCalls = getActiveCalls();

        if(dialog == null)
        {
            logger.debug("Cannot find a peer with a null dialog. "
                        +"Returning null");
            return null;
        }

        if(logger.isTraceEnabled())
        {
            logger.trace("Looking for peer with dialog: " + dialog
                        + " among " + this.activeCalls.size() + " calls");
        }


        while(activeCalls.hasNext())
        {
            Call call = activeCalls.next();
            if(call.getDialog().getCallId().equals(dialog.getCallId()))
                return call;
        }

        return null;
    }
    
    public Call findCall(String callId)
    {
        Iterator<Call> activeCalls = getActiveCalls();

        if(callId == null)
        {
            logger.debug("Cannot find a peer with a null dialog. "
                        +"Returning null");
            return null;
        }

        if(logger.isTraceEnabled())
        {
            logger.trace("Looking for peer with dialog: " + callId
                        + " among " + this.activeCalls.size() + " calls");
        }


        while(activeCalls.hasNext())
        {
            Call call = activeCalls.next();
            if(call.getCallID().equals(callId))
                return call;
        }

        return null;
    }

}
